//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// Change type/kind attribute of object.
//

# include "StdAfx.h"
# include "Resource.h"
# include "ChangeType.h"
# include "ChangeTypeSet.h"
# include "SelectType.h"
# include "Types.h"
# include "MapView.h"
# include "Load.h"
# include "Selection.h"
# include "UndoRedo.h"
# include "Options.h"
# include "Label.h"
# include "CombinedAction.h"
# include "Globals.h"

class CChangeTypeAction : public CEditAction {
  public:
	CChangeTypeAction (rgn_t & _rgn, USHORT _ushType) :
		m_rgn (_rgn),
		m_ushPrevType (_rgn.GetType ()),
		m_ushNewType (_ushType),
		m_bWasModified ((_rgn.dwFlags & rgn_t::fModified) != 0)
	{
		CCritSec::CLock lock (g_cs);

		m_strName.append ("change type [");
		GetHintText (m_rgn, m_strName);
		m_strName.append ("]");
	}

	virtual void GetName (string_t & _str) const {_str.append (m_strName);}
	virtual cu_t CanUndo () const {return cuYes;}
	virtual bool Do ();
	virtual void Undo ();
	virtual void Redo ();

  private:
	rgn_t & m_rgn;

	USHORT const m_ushNewType;
	USHORT const m_ushPrevType;

	string_t m_strName;
	bool const m_bWasModified;
};

bool CChangeTypeAction::Do () {
	CCritSec::CLock lock (g_cs);

	// Change type.
	m_rgn.SetType (g_map.pTypeSet, m_ushNewType, m_rgn.Kind);
	m_rgn.RefreshAttributes (g_map, false);

	m_rgn.dwFlags |= rgn_t::fModified;

	return true;
}

void CChangeTypeAction::Undo () {
	CCritSec::CLock lock (g_cs);

	// Revert type.
	m_rgn.SetType (g_map.pTypeSet, m_ushPrevType, m_rgn.Kind);
	m_rgn.RefreshAttributes (g_map, false);

	if (! m_bWasModified)
		m_rgn.dwFlags &= ~rgn_t::fModified;
}

void CChangeTypeAction::Redo () {
	CChangeTypeAction::Do ();
}

void ChangeType (rgn_t & _rgn, USHORT _ushType) {
	PerformEditAction (new CChangeTypeAction (_rgn, _ushType));
}

/////////////////////////////////////////////////////////////////

class CConvertPoly : public CEditAction {
  public:
	CConvertPoly (rgn_t & _rgn, USHORT _ushType, RGN_KIND _Kind) :
		m_rgn (_rgn),
		m_pRgnNew (NULL),
		m_ushType (_ushType),
		m_Kind (_Kind)
	{
		CCritSec::CLock lock (g_cs);

		m_strName.append (_Kind == rkPolygon ? "convert to polyline [" : "convert to polygon [");
		GetHintText (m_rgn, m_strName);
		m_strName.append ("]");
	}

	virtual void GetName (string_t & _str) const {_str.append (m_strName);}
	virtual cu_t CanUndo () const {return cuYes;}
	virtual bool Do ();
	virtual void Undo ();
	virtual void Redo ();

  private:
	rgn_t & m_rgn;
	rgn_t * m_pRgnNew;
	USHORT const m_ushType;
	RGN_KIND const m_Kind;
	string_t m_strName;
};

bool CConvertPoly::Do () {
	CCritSec::CLock lock (g_cs);

	assert (m_pRgnNew == NULL);

	m_pRgnNew = & g_map.CreateRgn (m_Kind);

	// Initialize the object.
	m_pRgnNew->dwFlags |= rgn_t::fModified;

	m_pRgnNew->strLabel   = m_rgn.strLabel;
	m_pRgnNew->strComment = m_rgn.strComment;

	m_pRgnNew->SetType (g_map.pTypeSet, m_ushType, m_Kind);

	m_pRgnNew->uchEndLevel = m_rgn.uchEndLevel;

	if (m_rgn.dwFlags & rgn_t::fDontFind)
		m_rgn.dwFlags |= rgn_t::fDontFind;

	if (m_rgn.pExtra) {
		m_pRgnNew->pExtra->pCity         = m_rgn.pExtra->pCity;
		m_pRgnNew->pExtra->pHighway      = m_rgn.pExtra->pHighway;
		m_pRgnNew->pExtra->pZip          = m_rgn.pExtra->pZip;
		m_pRgnNew->pExtra->strStreetDesc = m_rgn.pExtra->strStreetDesc;
		m_pRgnNew->pExtra->strNumber     = m_rgn.pExtra->strNumber;
		m_pRgnNew->pExtra->strPhone      = m_rgn.pExtra->strPhone;
		m_pRgnNew->pExtra->strExtra      = m_rgn.pExtra->strExtra;
	}

	// Copy elements.
	const rgn_elements_t & elements = m_rgn.elements;
	const size_t cElements = elements.size ();
	m_pRgnNew->elements.resize (cElements);
	for (size_t cElement = 0; cElement < cElements; ++ cElement) {
		const rgn_element_t & element = elements [cElement];
		rgn_element_t & elementNew = m_pRgnNew->elements [cElement];
		if (element.bDeleted) {
			elementNew.bDeleted = true;
			continue;
		}

		elementNew.cFileOffset = -1;
		elementNew.cLevel      = element.cLevel;
		elementNew.bHole       = element.bHole;
		elementNew.points      = element.points;

		if (m_Kind == rkPolyline)
			// Close polyline.
			elementNew.points.push_back (elementNew.points [0]);
		else {
			assert (m_Kind == rkPolygon);

			// Remove coinciding first and last points, if any.
			if (elementNew.points.size () > 0 && elementNew.points [0] == elementNew.points [elementNew.points.size () - 1])
				elementNew.points.pop_back ();
		}
	}

	if (m_rgn.pExtra)
		m_pRgnNew->pExtra = m_rgn.pExtra;

	m_pRgnNew->RefreshAttributes (g_map);
	if (m_Kind == rkPolygon)
		m_pRgnNew->ReorderHoles ();

	// Delete the original object.
	m_rgn.Delete (g_map);

	return true;
}

void CConvertPoly::Undo () {
	CCritSec::CLock lock (g_cs);

	assert (m_pRgnNew != NULL);
	m_pRgnNew->Delete (g_map);

	m_rgn.Undelete (g_map);
}

void CConvertPoly::Redo () {
	CCritSec::CLock lock (g_cs);

	assert (m_pRgnNew != NULL);
	m_pRgnNew->Undelete (g_map);

	m_rgn.Delete (g_map);
}

/////////////////////////////////////////////////////////////////

void ChangeType (rgn_t & _rgn, USHORT _ushType, RGN_KIND _Kind) {
	const RGN_KIND PrevKind = static_cast<RGN_KIND> (_rgn.Kind);
	if (_Kind == PrevKind)
		ChangeType (_rgn, _ushType);
	else {
		if (_Kind == rkPolyline)
			assert (PrevKind == rkPolygon);
		else if (_Kind == rkPolygon)
			assert (PrevKind == rkPolyline);
		else {
			assert (false);
			return;
		}
		PerformEditAction (new CConvertPoly (_rgn, _ushType, _Kind));
	}
}

static
void _ApplyChangeTypeForSelection (USHORT _ushType, RGN_KIND _Kind) {
	// Apply to all selected items.
	const int iDepth = BeginCombinedAction ();

	const size_t cSelItems = g_Selection.size ();
	for (size_t cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
		find_info_t & SelItem = g_Selection [cSelItem];
		if (SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && ! SelItem.iRgn->IsWTR ()) {
			rgn_t & rgn = * SelItem.iRgn;
			if (rgn.ushType != _ushType || rgn.Kind != _Kind || (rgn.pTypeDesc && rgn.pTypeDesc->bUnknown))
				ChangeType (rgn, _ushType, _Kind);
		}
	}

	EndCombinedAction (iDepth, GetStr (IDS_CHANGE_TYPE_MULTIPLE));

	Refresh ();
}

/////////////////////////////////////////////////////////////////

/*
static
INT_PTR CALLBACK SelectTypeSetProc (HWND _hWnd, UINT _uMsg, WPARAM _wParam, LPARAM _lParam) {
	switch (_uMsg) {
		case WM_INITDIALOG: {
			return TRUE;
		}

		case WM_COMMAND:
			switch (LOWORD (_wParam)) {
				case IDOK:
					::EndDialog (_hWnd, 0);
					return TRUE;

				case IDCANCEL:
					::EndDialog (_hWnd, 1);
					return TRUE;
			}
			
	}
	return FALSE;
}*/

bool ShowChangeType (HWND _hWnd, RGN_KIND _Kind, USHORT & _ushType) {
	const CTypeSet * pTypeSet = NULL;
	{
		CCritSec::CLock lock (g_cs);
		if (g_map.pTypeSet == & g_tsNull)
			ChangeTypeSet (GetDefaultTypeSet (), false);

		pTypeSet = g_map.pTypeSet;
	}

	return SelectType (_hWnd, pTypeSet, _Kind, _ushType, _ApplyChangeTypeForSelection);
}

//////////////////////////////////////////////////

bool IsChangeTypeAppliable () {
	if (g_bLoading)
		return false;

	bool bAppliable = false;
	BYTE Kind = 0;

	const size_t cSelItems = g_Selection.size ();
	for (size_t cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
		find_info_t & SelItem = g_Selection [cSelItem];
		if (SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && ! SelItem.iRgn->IsWTR ()) {
			if (SelItem.cElementOrOrigin != -1)
				return false;

			if (! bAppliable) {
				bAppliable = true;
				Kind = SelItem.iRgn->Kind;
			} else {
				// Make sure that all seleted items have the same kind.
				if (Kind != SelItem.iRgn->Kind)
					return false;
			}
		}
	}

	return bAppliable;
}

static USHORT g_ushPrevTypes [rkMax];

void ShowChangeTypeForSelection (HWND _hWnd) {
	if (! IsChangeTypeAppliable ())
		return;

	// Deternine kind & type of currently selected objects.
	USHORT ushType = 0;
	RGN_KIND Kind = rkPoint;
	const size_t cSelItems = g_Selection.size ();
	for (size_t cSelItem = 0; cSelItem < cSelItems; ++ cSelItem) {
		find_info_t & SelItem = g_Selection [cSelItem];
		if (SelItem.pLayer && ! SelItem.iRgn->IsDeleted () && ! SelItem.iRgn->IsWTR ()) {
			ushType = SelItem.iRgn->GetType ();
			Kind = static_cast<RGN_KIND> (SelItem.iRgn->Kind);
			break;
		}
	}
	assert (Kind < rkMax);

	if (ShowChangeType (_hWnd, Kind, ushType)) {
		_ApplyChangeTypeForSelection (ushType, Kind);
		g_ushPrevTypes [Kind] = ushType;
	}
}

void ShowChangeTypeForSelection (HWND _hWnd, RGN_KIND _Kind) {
	if (! IsChangeTypeAppliable ())
		return;

	if (ShowChangeType (_hWnd, _Kind, g_ushPrevTypes [_Kind]))
		_ApplyChangeTypeForSelection (g_ushPrevTypes [_Kind], _Kind);
}
